package io.kiki.stack.netty.channelManager;

import io.kiki.stack.netty.exception.RemotingException;
import io.kiki.stack.netty.lifeCycle.LifeCycle;

import java.util.List;
import java.util.Map;

public interface ChannelManager extends ChannelHeartbeatManager, LifeCycle {

    /**
     * Deprecated, use startup instead.
     */
    @Deprecated
    void init();

    /**
     * Add a Channel to {@link Pool}.
     * If it contains multiple pool keys, this Channel will be added to multiple {@link Pool} too.
     *
     * @param channel an available Channel, you should {@link #check(Channel)} this Channel before add
     */
    void add(Channel channel);

    /**
     * Add a Channel to {@link Pool} with the specified poolKey.
     *
     * @param channel an available Channel, you should {@link #check(Channel)} this Channel before add
     * @param poolKey unique key of a {@link Pool}
     */
    void add(Channel channel, String poolKey);

    /**
     * Get a Channel from {@link Pool} with the specified poolKey.
     *
     * @param poolKey unique key of a {@link Pool}
     * @return a {@link Channel} selected by {@link ChannelSelectStrategy}<br>
     * or return {@code null} if there is no {@link Pool} mapping with poolKey<br>
     * or return {@code null} if there is no {@link Channel} in {@link Pool}.
     */
    Channel get(String poolKey);

    /**
     * Get all Channel from {@link Pool} with the specified poolKey.
     *
     * @param poolKey unique key of a {@link Pool}
     * @return a list of {@link Channel}<br>
     * or return an empty list if there is no {@link Pool} mapping with poolKey.
     */
    List<Channel> getAll(String poolKey);

    /**
     * Get all channels of all poolKey.
     *
     * @return a map with poolKey as key and a list of channels in channelPool as value
     */
    Map<String, List<Channel>> getAll();

    /**
     * Remove a {@link Channel} from all {@link Pool} with the poolKeys in {@link Channel}, and close it.
     */
    void remove(Channel channel);

    /**
     * Remove and close a {@link Channel} from {@link Pool} with the specified poolKey.
     *
     * @param channel target channel
     * @param poolKey unique key of a {@link Pool}
     */
    void remove(Channel channel, String poolKey);

    /**
     * Remove and close all channel from {@link Pool} with the specified poolKey.
     *
     * @param poolKey unique key of a {@link Pool}
     */
    void remove(String poolKey);

    /**
     * Remove and close all channel from all {@link Pool}.
     * Deprecated, use shutdown instead.
     */
    @Deprecated
    void removeAll();

    /**
     * check a channel whether available, if not, throw RemotingException
     *
     * @param channel target channel
     */
    void check(Channel channel) throws RemotingException;

    /**
     * Get the number of {@link Channel} in {@link Pool} with the specified pool key
     *
     * @param poolKey unique key of a {@link Pool}
     * @return channel count
     */
    int count(String poolKey);

    /**
     * Get a channel using {@link Url}, if {@code null} then create and add into {@link Pool}.
     * The channel number of {@link Pool} is decided by {@link Url#getChannelNum()}
     *
     * @param url {@link Url} contains connect infos.
     * @return the created {@link Channel}.
     * @throws InterruptedException if interrupted
     * @throws RemotingException    if create failed.
     */
    Channel getAndCreateIfAbsent(Url url) throws InterruptedException, RemotingException;

    /**
     * This method can create channel pool with channels initialized and check the number of channels.
     * The channel number of {@link Pool} is decided by {@link Url#getChannelNum()}.
     * Each time call this method, will check the number of channel, if not enough, this will do the healing logic additionally.
     */
    void createChannelAndHealIfNeed(Url url) throws InterruptedException, RemotingException;

    // ~~~ create operation

    /**
     * Create a channel using specified {@link Url}.
     *
     * @param url {@link Url} contains connect infos.
     */
    Channel create(Url url) throws RemotingException;

    /**
     * Create a channel using specified {@link String} address.
     *
     * @param address        a {@link String} address, e.g. 127.0.0.1:1111
     * @param connectTimeout an int connect timeout value
     * @return the created {@link Channel}
     * @throws RemotingException if create failed
     */
    Channel create(String address, int connectTimeout) throws RemotingException;

    /**
     * Create a channel using specified ip and port.
     *
     * @param ip             connect ip, e.g. 127.0.0.1
     * @param port           connect port, e.g. 1111
     * @param connectTimeout an int connect timeout value
     * @return the created {@link Channel}
     */
    Channel create(String ip, int port, int connectTimeout) throws RemotingException;

    // ~~~ create channel managed by channelManager

    /**
     * Create a channel in asynchronous way and add to {@link Pool}.
     *
     * @param url {@link Url} contains connect infos.
     * @throws RemotingException if create failed
     */
    void asyncCreateChannel(Url url) throws InterruptedException, RemotingException;

    void scan();
}
